package model.tokens;

import java.util.Map;
import java.util.TreeMap;
import java.util.List;
import java.util.HashMap;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;

import model.parser.ParserException;
import util.reflection.Reflection;
import util.reflection.ReflectionException;
import util.resources.ResourceManager;
import model.parser.*;

/**
 * The TokenFactory takes a string and creates a token based on which regular
 * expression it matches.
 */
public class TokenFactory<T> {
    private Map<String, List<String>> regexToTokenClass;
    private Map<String, Integer> priority;
    private FunctionTable<T> myFunctionTable;

    public TokenFactory(String tokenResourceFile, FunctionTable<T> functionTable) {
        ResourceManager resources = ResourceManager
                .getManager(tokenResourceFile);

        // Create the map of regular expressions representing tokens, ordered by
        // priority of
        // evaluation.
        regexToTokenClass = new TreeMap<String, List<String>>(
                new MapComparator());
        priority = new HashMap<String, Integer>();

        for (String pattern : resources) {
            String[] tokens = resources.getStringArray(pattern);
            priority.put(pattern, Integer.parseInt(tokens[0]));
            List<String> tokenClassNames = Arrays.asList(Arrays.copyOfRange(
                    tokens, 1, tokens.length));
            regexToTokenClass.put(pattern, tokenClassNames);
        }

        myFunctionTable = functionTable;
    }

    /**
     * Gets the token map.
     * 
     * @return the unmodifiable token map
     */
    public Map<String, List<String>> getRegexTokenMap() {
        return Collections.unmodifiableMap(regexToTokenClass);
    }

    /**
     * Create a token from a string.
     * 
     * @param symbol
     *            a string representing a token.
     * @return a token.
     */
    @SuppressWarnings("unchecked")
    public Token<T> makeToken(String symbol, Token<T> lastToken) {
        TokenConstructorArguments<T> constructorArgs = new TokenConstructorArguments<T>(
                symbol, myFunctionTable, lastToken);
        for (String regex : regexToTokenClass.keySet()) {
            if (symbol.matches(regex)) {
                for (String className : regexToTokenClass.get(regex)) {
                    try {
                        return (Token<T>) Reflection.createInstance(className,
                                constructorArgs);
                    } catch (ReflectionException e) {
                        /*
                         * Right now, if we weren't able to instantiate the
                         * class, assume that the symbol didn't really match the
                         * token (for instance the symbol wasn't a function
                         * name) and continue trying to match.
                         */
                        continue;
                    }
                }
            }
        }

        throw ParserException.BAD_TOKEN;
    }

    /**
     * Used to order keys in the resource bundle by their priority (specified in
     * the bundle), for iteration.
     */
    private class MapComparator implements Comparator<String> {

        @Override
        public int compare(String arg0, String arg1) {
            return priority.get(arg0).compareTo(priority.get(arg1));
        }
    }
}
